package cn.ibizlab.util.service;

import cn.ibizlab.util.client.UaaFeignClient;
import cn.ibizlab.util.errors.BadRequestAlertException;
import cn.ibizlab.util.errors.InternalServerErrorException;
import cn.ibizlab.util.security.*;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 实体[UAA20] 服务对象接口实现
 */
@Service("IBZUAAUserService")
@ConditionalOnExpression(" '${ibiz.ref.service.rt.version:}'.equals('RT2') || '${ibiz.auth.service:Uaa20UserService}'.equals('Uaa20UserService') ")
public class Uaa20UserService implements UserServiceAdapter{

    @Value("${ibiz.jwt.secret:MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKaTz4pgOR/p0RjlczM0dErf4ZUNziE/HJcfKDLPu77Gs2EJdDK0uGfPI3GX/eRwX9L9bTZJtz9sX2fkRqRt3gWnsMypT2P/cO/2GgtRCPHRFD7BI+Df32isEJZ6M4kD9tyKLw8Y9KuP0C20ZMMDeCrrbjMagMMrwTOM4/4eFjzVAgMBAAECgYEAnH3mj2hgolOmhg4hkOxpiGIV6lMi4OcKtAqoWDwCdHL12GbqTCytxZC7Cp+w/Wh5DZ3aeRL93c6xPsgdeaJh3kYa4ooo6b5tFHPU63VU5MBgwGzi26/6GB4GCXxGMB+SxmdigDmmPIYbXD+jO2oj1s8hj+DOE4U2fIjeZ//DumECQQDlA74KHNZlKxoWl0FoHCgcIHFBZcQWKO3puhrH7VsRYI7CVVguE57NBT6QvAmU9r32PDt64tS0Qd1sCrk4uEqtAkEAujSj/cwF4ctQZCbUoMMzK/mw8ZxW3M6VK3urbq8fyFJ2iT2aLV3jE3+tnDdpezcfnbs/9SVXeFmJpdg/L7hnyQJAQyxo1qCExmHxIgU1uyrfHPjrH2qRLIrO1gqvhkr5tkwjM59C4SkCIFLUejGdgeMp7wrVy4KzLzhOkT1H/PoZdQJAEbKxJ409veFKKcq7CPCkq3hXBg/a/a+w4+okOCfy+GJGG/M79TXoQFExWhi2MNzjZ2WFxbIf5zNzbszn7Iw1aQJBAIXpKrQ0sLxr0rFzsoHBrobPcnQeutzypQNTPkoItILVP9pWz+aUfIKnOHEC5GAdD2LPZZ/pF7ixdRd9nBab+pk=}")
    private String secret;
    @Value("${ibiz.jwt.expiration:7200000}")
    private Long expiration;
    @Value("${ibiz.jwt.signature:MAC}")
    private String signature;
    @Autowired
    private UaaFeignClient uaaFeignClient;
    protected SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    protected static final String SIGNATURE_RSA = "RSA";
    protected static final String SIGNATURE_MAC = "MAC";

    @Override
    public AuthenticationInfo loadUserByLogin(String username, String password) {
        String[] data = username.split("[|]");
        String loginname = username;
        String domains = "";

        if(data.length==2) {
            loginname=data[0].trim();
            domains=data[1].trim();
        }
        AuthorizationLogin logininfo = new AuthorizationLogin();
        logininfo.setDomain(domains);
        logininfo.setLoginname(loginname);
        logininfo.setPassword(password);
        AuthenticationInfo info = uaaFeignClient.login(logininfo);
        if(info.getUser() == null){
            throw new BadRequestAlertException("登录失败","IBZUAAUser", username);
        }
        return info;
    }

    @Override
    public AuthenticationUser loadUserByUsername(String username) {
        return uaaFeignClient.loadUserByUsername(username);
    }


    @Override
    public Map<String,Object> getAppData(String systemId, boolean isEnablePermissionValid) {

        Map<String,Object> appData = new HashMap<>() ;
        Set<String> appMenu = new HashSet<>();
        Set<String> uniRes = new HashSet<>();
        AuthenticationUser user = AuthenticationUser.getAuthenticationUser();
        Collection<GrantedAuthority> authorities = user.getAuthorities();
        if(isEnablePermissionValid && !ObjectUtils.isEmpty(systemId) && !ObjectUtils.isEmpty(authorities)){
            authorities.forEach(item->{
                String authority=item.getAuthority();
                if(authority.startsWith(systemId))
                    uniRes.add(authority);
                if (authority.startsWith(String.format("%1$s-APPMENU",systemId)))
                    appMenu.add(authority.substring(systemId.length()+9));
            });
        }
        if (!ObjectUtils.isEmpty(user.getExpiration())) {
            appData.put("expireddate", dtFormat.format(user.getExpiration()));
        }
        Map<String,Object> context = new HashMap<>();
        context.putAll(user.getSessionParams());
        context.put("srfusername",user.getDisplayName());
        appData.put("context",context);
        appData.put("unires",uniRes);
        appData.put("appmenu",appMenu);
        appData.put("enablepermissionvalid", !user.isSuperUser() && isEnablePermissionValid);
        return appData;
    }

    @Override
    public Map<String,List<String>> getOrgInfo(String organizationId, String departmentId) {
        return uaaFeignClient.getOrgInfo(organizationId, departmentId);
    }

    /**
     * 生成token
     * @param userDetails
     * @return
     */
    @Override
    public String generateToken(UserDetails userDetails) {
        if(ObjectUtils.isEmpty(signature)|| SIGNATURE_MAC.equalsIgnoreCase(signature))
            return generateTokenByMAC(userDetails);
        else
            throw new InternalServerErrorException(String.format("生成访问令牌出错，签名暂未支持[%1$s]加密算法",signature));
    }

    /**
     * 通过MAC方式生成token（对称加密）
     * @param userDetails
     * @return
     */
    @SneakyThrows
    protected String generateTokenByMAC(UserDetails userDetails) {
        Date now = new Date();
        JWSSigner signer = new MACSigner(getSecret());
        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS512)
                .contentType("JWS")
                .build();
        JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                .subject(userDetails.getUsername())
                .issueTime(now)
                .expirationTime(new Date(now.getTime()+getExpiration()))
                .build();
        SignedJWT signedJWT = new SignedJWT(header,claimsSet);
        signedJWT.sign(signer);
        return signedJWT.serialize();
    }

    /**
     * 验证token
     * @param token
     * @param userDetails
     * @return
     */
    @Override
    public Boolean validateToken(String token, UserDetails userDetails) {
        if(ObjectUtils.isEmpty(signature)|| SIGNATURE_MAC.equalsIgnoreCase(signature))
            return validateTokenByMAC(token,userDetails);
        else if(SIGNATURE_RSA.equalsIgnoreCase((signature)))
            return validateTokenByRSA(token,userDetails);
        else
            throw new InternalServerErrorException(String.format("验证访问令牌出错，签名暂未支持[%1$s]加密算法",signature));
    }

    /**
     * 通过MAC方式验证token（对称加密）
     * @param token
     * @param userDetails
     * @return
     */
    @SneakyThrows
    protected Boolean validateTokenByMAC(String token, UserDetails userDetails) {
        Date now = new Date();
        JWSVerifier verifier = new MACVerifier(getSecret());
        SignedJWT signedJWT = SignedJWT.parse(token);
        JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
        if(signedJWT.verify(verifier) && now.before(claimsSet.getExpirationTime())){
            if(userDetails instanceof AuthenticationUser){
                AuthenticationUser user = (AuthenticationUser) userDetails;
                user.setToken(token);
                user.setExpiration(claimsSet.getExpirationTime());
            }
            return true;
        }
        return false;
    }

    /**
     * 通过RSA方式验证token（非对称加密）
     * @param token
     * @param userDetails
     * @return
     */
    @SneakyThrows
    protected Boolean validateTokenByRSA(String token, UserDetails userDetails){
        String signatureKey = uaaFeignClient.signatureKey();
        if(ObjectUtils.isEmpty(signatureKey))
            throw new BadRequestAlertException("验证访问令牌出错，未能获取到公钥信息", "Token", token);

        Date now = new Date();
        RSAKey publicJWK = RSAKey.parse(signatureKey);
        JWSVerifier verifier = new RSASSAVerifier(publicJWK);
        SignedJWT signedJWT = SignedJWT.parse(token);
        JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();

        if(!signedJWT.verify(verifier))
            return false;

        if(now.after(claimsSet.getExpirationTime()))
            throw new BadRequestAlertException(String.format("访问令牌已过期，令牌有效期为[%1$s]，当前时间为[%2$s]",sdf.format(claimsSet.getExpirationTime()),sdf.format(now)), "Token", token);

        AuthenticationUser user = (AuthenticationUser) userDetails;
        user.set("token", token);
        user.set("expiration",claimsSet.getExpirationTime());
        return true;
    }

    @Override
    public String getSecret() {
        return secret;
    }

    @Override
    public Long getExpiration() {
        return expiration;
    }


}
